import React from "react";
import { Tooltip, Button, Input, Empty } from "antd";
import { SettingOutlined, SkinOutlined, PieChartOutlined } from "@ant-design/icons";
import { connect } from "react-redux";
import { HTML5Backend } from 'react-dnd-html5-backend'
import { DndProvider, DropTargetMonitor } from "react-dnd";
import { v4 as uuid } from "uuid";
import { cloneDeep, debounce, get, set } from "lodash";
import VisbarGUI, { extractDefault } from "visbar-gui";
import { materialConfig as visbarComponentConfig } from "visbar-component";
import DragWrapper from "../../components/DragWrapper";
import DropWrapper from "../../components/DropWrapper";
import DragSortList from "../../components/DragSortList";
import Controls from "./components/Controls";
import RenderForConfig from "./components/RenderForConfig";
import { materialData } from "../../constants/material";
import materialConfiguration from "../../constants/materialConfiguration";
import { generatorSeriesConfig, generatorDataSourceConfig, transformDataSourceBaseConfig } from "./utils";
import pageConfig from "../../constants/page.json";
import { IEditorState as IEditorReducerState, IComponent as IComponentState } from "../../store/reducers/editor";
import {
  ADD_COMPONENT, SELECT_ELEM, SET_CURRENT_ELEMENT_PROPS,
  DELETE_COMPONENT, DATASOURCE_KEY, RESET_COMPONENT, SET_COMPONENT_NAME
} from "../../store/constants";
import "./index.scss";

const Component = React.PureComponent || React.Component;
const materialControlList = Object.keys(materialData).map(key => ({ ...materialData[key], key }));
const configMenu = [{ name: "选项配置", icon: SettingOutlined, key: "props" }, { name: "样式配置", icon: SkinOutlined, key: "style" }, { name: "数据配置", icon: PieChartOutlined, key: DATASOURCE_KEY }];
const noResponseKeyBoardElem = ["TEXTAREA", "INPUT", ""];
interface IEditorState {
  activeCategory: string;
  activeConfigMenuKey: string;
  editingComponentName: string;  // 编辑组件名的id
}
interface IEditorProps {
  addComponent: (component: any) => any;
  selectElement: (id: string) => any;
  setCurrentElementProps: (payload: any) => any;
  deleteComponent: (id?: string) => any;
  resetComponent: (components: any) => any;
  setComponentName: (payload: any) => any;
}

class Editor extends Component<IEditorProps & IEditorReducerState, IEditorState> {
  editorWrapper = React.createRef<HTMLElement>();
  canvasWrapper: any = React.createRef();
  constructor(props: any) {
    super(props);
    this.state = {
      activeCategory: materialControlList[0].key,
      activeConfigMenuKey: "props",
      editingComponentName: ""
    }
  }
  /* 获取拖拽放置位置偏移 */
  getDropPosition = ({ x, y }: { x?: number, y?: number }) => {
    if (this.canvasWrapper.current) {
      const { top, left } = this.canvasWrapper.current?.getBoundingClientRect() || {};
      return { top: y ? y - top : 0, left: x ? x - left : 0 };
    }
    return { top: 0, left: 0 };
  }
  /* 添加组件 */
  addComponent = (data: any, monitor: DropTargetMonitor) => {
    const { components, addComponent } = this.props;
    const materialConfig = data.node ? (visbarComponentConfig as any)[data.node]?.eVisbar || {} : materialConfiguration[data.key];
    const newComponent: IComponentState = {
      name: data.name,
      node: data.node,
      key: data.key,
      id: uuid(),
      index: components.length,
      props: extractDefault({ config: materialConfig.props }),
      style: extractDefault({ config: materialConfig.style, value: this.getDropPosition(monitor.getClientOffset() || {}) }),
      dataSource: {}
    };
    addComponent(newComponent);
  }
  /* 选中当前组件 */
  selectElemHandler = (id: string, _: any) => {
    const { selectElement } = this.props;
    selectElement(id);
  }
  /* 取出当前的物料配置 */
  getMaterialConfig = () => {
    const { currentElement, components } = this.props;
    const { activeConfigMenuKey } = this.state;
    const comp = components.find(com => com.id === currentElement);
    if (comp && comp.node) {
      const visbarConfig = (visbarComponentConfig as any)[comp.node]?.eVisbar || {};
      return visbarConfig[activeConfigMenuKey] || {};
    }
    if (activeConfigMenuKey === DATASOURCE_KEY && comp) {
      const baseConfig = materialConfiguration[comp.key]?.[activeConfigMenuKey] || {};
      return Object.assign({},
        transformDataSourceBaseConfig(cloneDeep(baseConfig), comp.dataSource),
        generatorDataSourceConfig(comp.dataSource?.[DATASOURCE_KEY] || []));
    }
    const seriesConfig = generatorSeriesConfig(comp?.props?.series || []);
    const result = (comp ? materialConfiguration[comp.key]?.[activeConfigMenuKey] : (pageConfig as any)[activeConfigMenuKey]) || {};
    result.series && (result.series.childrenList = seriesConfig);
    return result;
  }
  /* gui配置项change事件 */
  guiConfigChangeHandler = (ev: any) => {
    let { setCurrentElementProps, currentElement, pageProps } = this.props;
    let { activeConfigMenuKey } = this.state;
    let currentCompProps = (this.getCurrentComponent() as any)?.[activeConfigMenuKey];
    !currentCompProps && (currentCompProps = pageProps?.[activeConfigMenuKey] || {});
    currentCompProps = cloneDeep(currentCompProps);
    set(currentCompProps, ev.path, ev.value);
    if (currentCompProps) {
      setCurrentElementProps({ type: activeConfigMenuKey, value: currentCompProps, activeId: currentElement })
    }
  }
  /** 获取当前控件信息 */
  getCurrentComponent = () => {
    const { components, currentElement, pageProps } = this.props;
    return components.find(comp => comp.id === currentElement) || pageProps;
  }
  /** 设置控件名 */
  setComponentNewName = (ev: React.KeyboardEvent<HTMLInputElement>) => {
    if (ev.code === "Enter") {
      const value = (ev.target as any).value;
      const { editingComponentName } = this.state;
      this.props.setComponentName({ id: editingComponentName, name: value });
      this.setState({ editingComponentName: "" });
    }
  }
  /* 点击canvas画布的空白区 */
  clickCanvasBlankArea = (ev: React.MouseEvent<any>) => {
    if (((ev.target as any)?.classList?.value || "").includes("canvas-main")) {
      this.props.selectElement("");
    }
  }
  keyBoardDistribute = (ev: KeyboardEvent) => {
    const { currentElement, deleteComponent } = this.props;
    const nodeName = (ev.target as any).nodeName;
    if (noResponseKeyBoardElem.includes(nodeName)) return;
    switch (ev.code) {
      case "Backspace":
      case "Delete":
        deleteComponent(currentElement)
    }
  }
  /* 设置当前编辑画布页面的宽高 */
  resizeEditorPageSize = () => {
    if (this.canvasWrapper.current) {
      const { pageProps, setCurrentElementProps } = this.props;
      const canvasWrapper = this.canvasWrapper.current;
      const newPageStyle = cloneDeep(pageProps.style || {});
      newPageStyle.width = canvasWrapper.offsetWidth;
      newPageStyle.height = canvasWrapper.offsetHeight;
      setCurrentElementProps({ type: "style", value: newPageStyle })
    }
  }
  debounceResizeEditorPageSize = debounce(this.resizeEditorPageSize, 100);
  componentDidMount() {
    window.addEventListener("keyup", this.keyBoardDistribute, true);
    window.addEventListener("resize", this.debounceResizeEditorPageSize);
    this.resizeEditorPageSize();
  }
  componentWillUnmount() {
    window.removeEventListener("keyup", this.keyBoardDistribute, true);
    window.removeEventListener("resize", this.debounceResizeEditorPageSize);
  }
  render() {
    const { components, pageProps, currentElement, blueprint, setCurrentElementProps, resetComponent } = this.props;
    const { activeCategory, activeConfigMenuKey, editingComponentName } = this.state;
    const guiVisbarConfig = this.getMaterialConfig();
    return <DndProvider backend={HTML5Backend}>
      <div className="editor">
        <header className="editor-header">
          Echarts-Visbar Editor
          <Button type="link" href="/#/preview" className="preview-button">预览</Button>
          <Button type="link" href="/#/blueprint" className="preview-button">蓝图</Button>
        </header>
        <main className="editor-main" ref={this.editorWrapper}>
          <aside className="outline-tree">
            <div className="current-control-list">
              {(materialData[activeCategory]?.control || [])
                .map((control: any) => <DragWrapper key={control.key} type="controls" itemData={control}><Controls {...control} /></DragWrapper>
              )}
            </div>
            <ul className="category-list">
              {materialControlList.map(config => {
                return <li className={`catetory-item ${config.key === activeCategory && "active-category"}`}
                  key={config.key}
                  onClick={() => this.setState({ activeCategory: config.key })}
                >{config.name}</li>
              }) }
            </ul>
          </aside>
          <aside className="current-component">
            {components.length > 0 ? <DragSortList data={components}
              renderItem={(item) => <div className="component-item" onDoubleClick={() => this.setState({ editingComponentName: item.id })}>
                {item.id === editingComponentName ? <Input type="text" defaultValue={item.name} onKeyDown={this.setComponentNewName} /> : item.name}
              </div>}
              onChange={resetComponent} /> : <div className="no-component-list"><Empty /></div>}
          </aside>
          <div className="canvas-main" onClick={this.clickCanvasBlankArea}>
              <div className="canvas-wrapper" ref={this.canvasWrapper}>
                <DropWrapper accept="controls" onDrop={this.addComponent}>
                  <RenderForConfig config={components}
                    pageConfig={pageProps}
                    onSelect={this.selectElemHandler}
                    onChange={(type, value, activeId) => setCurrentElementProps({ type, value, activeId })}
                    activeId={currentElement}
                    blueprint={blueprint}
                  />
                </DropWrapper>
              </div>
          </div>
          <aside className="gui-config-wrapper">
            <ul className="config-header-menu">
              {configMenu.map(conf => <li
                className={`header-menu-item ${conf.key === activeConfigMenuKey && "active-menu-item"}`}
                key={conf.key}
                onClick={() => this.setState({ activeConfigMenuKey: conf.key })}>
                {<Tooltip title={conf.name}><conf.icon /></Tooltip>}
              </li>)}
            </ul>
            <VisbarGUI config={guiVisbarConfig}
              value={extractDefault({ config: guiVisbarConfig, value: get(this.getCurrentComponent(), activeConfigMenuKey) })}
              onChange={this.guiConfigChangeHandler}
            />
          </aside>
        </main>
      </div>
    </DndProvider>
  }
}

export default connect((state: any) => state.editor, (dispatch) => {
  return {
    addComponent: (component: any) => dispatch({ type: ADD_COMPONENT, payload: component }),
    selectElement: (id: string) => dispatch({ type: SELECT_ELEM, payload: id }),
    setCurrentElementProps: (payload: any) => dispatch({ type: SET_CURRENT_ELEMENT_PROPS, payload }),
    deleteComponent: (id?: string) => dispatch({ type: DELETE_COMPONENT, payload: id }),
    resetComponent: (components: any) => dispatch({ type: RESET_COMPONENT, payload: components }),
    setComponentName: (payload: any) => dispatch({ type: SET_COMPONENT_NAME, payload })
  }
})(Editor)